package com.miraclesea.component.log;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.miraclesea.framework.exception.LogicException;
import com.miraclesea.framework.exception.UserException;

@Aspect
@Component("loggerAspect")
public final class LoggerAspect {
	
	private static final Logger TRACE_LOG = LoggerFactory.getLogger("Trace");
	private static final Logger PERFORMANCE_LOG = LoggerFactory.getLogger("Performance");
	private static final Logger ERROR_LOG = LoggerFactory.getLogger("Error");
	
	@Around("com.miraclesea.framework.aspect.PublicPoint.globalPonit()")
	public Object aroundLogger(final ProceedingJoinPoint pjp) throws Throwable {
		CallTrace callTrace = new CallTrace(pjp);
		traceSignatureInfo(callTrace);
		traceArgsInfo(callTrace);
		PerformanceTrace performanceTrace = new PerformanceTrace(pjp);
		Object returnValue;
		try {
			returnValue = pjp.proceed();
		} catch (final UserException e) {
			tracePerformanceInfo(performanceTrace);
			traceUserExceptionInfo(callTrace, e);
			throw e;
		} catch (final LogicException e) {
			tracePerformanceInfo(performanceTrace);
			traceLogicExceptionInfo(callTrace, e);
			throw e;
		} catch (final Throwable e) {
			tracePerformanceInfo(performanceTrace);
			logUnexpectedExceptionInfo(new ErrorTrace(e, pjp));
			throw e;
		}
		tracePerformanceInfo(performanceTrace);
		traceReturnValueInfo(callTrace, returnValue);
		return returnValue;
	}
	
	private void traceSignatureInfo(final CallTrace callTrace) {
		if (TRACE_LOG.isTraceEnabled()) {
			TRACE_LOG.trace(callTrace.getSignatureInfo());
		}
	}
	
	private void traceArgsInfo(final CallTrace callTrace) {
		if (TRACE_LOG.isTraceEnabled()) {
			TRACE_LOG.trace(callTrace.getArgumentsInfo());
		}
	}
	
	private void traceUserExceptionInfo(final CallTrace callTrace, final UserException e) {
		if (TRACE_LOG.isTraceEnabled()) {
			TRACE_LOG.trace(callTrace.getUserExceptionInfo(e));
		}
	}
	
	private void traceLogicExceptionInfo(final CallTrace callTrace, final LogicException e) {
		if (TRACE_LOG.isTraceEnabled()) {
			TRACE_LOG.trace(callTrace.getLogicExceptionInfo(e));
		}
	}
	
	private void traceReturnValueInfo(final CallTrace callTrace, final Object returnValue) {
		if (TRACE_LOG.isTraceEnabled()) {
			TRACE_LOG.trace(callTrace.getReturnInfo(returnValue));
		}
	}
	
	private void tracePerformanceInfo(final PerformanceTrace performanceTrace) {
		if (PERFORMANCE_LOG.isTraceEnabled()) {
			PERFORMANCE_LOG.trace(performanceTrace.getPerformanceInfo());
		}
	}
	
	private void logUnexpectedExceptionInfo(final ErrorTrace errorTrace) {
		ERROR_LOG.error(errorTrace.getExceptionInfo(), errorTrace.getException());
	}
}
